/*
 * Presenter.java
 * 
 * @version 1.0, March 31, 2011
 * CS 2340 Team 10: Cyberdyne Systems
 */
package edu.gatech.oad.cyberdoc.presenter;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import edu.gatech.oad.cyberdoc.net.ClientConnectionHandler;
import edu.gatech.oad.cyberdoc.net.ResponseListener;
import edu.gatech.oad.cyberdoc.view.MainFrame;

/**
 * Contains most of the logic for the client-side of the application. All action
 * events are received by this class and all server interactions go through this
 * class.
 * 
 * @author John Girata
 * @version 1.0, March 31, 2011
 */
public class Presenter implements ResponseListener {

	/**
	 * The logger for this class.
	 */
	private static final Logger LOG;

	/**
	 * The singleton instance of this class.
	 */
	private static Presenter Instance;

	/**
	 * The username of the currently logged-in user, or null if no user is
	 * logged in.
	 */
	private String username;

	/**
	 * The password of the currently logged-in user, or null if no user is
	 * logged in.
	 */
	private String password;

	/**
	 * The type of user this is (one of "doctor", "staff", "patient", or
	 * "admin") or null if no user is logged in
	 */
	private String usertype;

	/**
	 * The most recently viewed day of appointments.
	 * 
	 * TODO Find a better way to do this
	 */
	private long midnight = 0;

	static {
		LOG = Logger.getLogger("edu.gatech.oad.cyberdoc.presenter.Presenter");

		Instance = null;
	}

	/**
	 * Hides the constructor and prevents other classes from creating instances
	 * of this class.
	 */
	private Presenter() {
		LOG.info("Creating Presenter instance");
	}

	/**
	 * The callback method to receive server responses.
	 * 
	 * @param response
	 *            The response from the server
	 * @see edu.gatech.oad.cyberdoc.net.ResponseListener#responseReceived(JSONObject)
	 */
	@Override
	public void responseReceived(JSONObject response) {
		try {

			if (response.has("action")) {
				LOG.info("Response received " + response);
				String next = response.getString("next");

				if (response.has("type")) {
					// TODO This probably shouldn't be a string
					usertype = response.getString("type");
					LOG.info("Logged in user is now a " + usertype);
				}

				if (next.equals("options")) {
					next += "_" + usertype;
				}

				MainFrame.getInstance().show(next, response);
			} else {
				LOG.warning("Error response received: " + response);
			}
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error reading response", e);
		}
	}

	/**
	 * Handles login events.
	 * 
	 * @param username
	 *            The username of the user attempting to log in
	 * @param password
	 *            The password of the user attempting to log in
	 */
	public void login(String username, String password) {
		LOG.info("Login event received");
		final JSONObject request = new JSONObject();

		this.username = username;
		this.password = password;

		try {
			request.put("username", username);
			request.put("password", password);

			send("login", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Handles request appointment events.
	 */
	public void requestAppointment() {
		LOG.info("Request appointment event received");
		final JSONObject request = new JSONObject();

		try {
			request.put("datetime", System.currentTimeMillis());

			send("request_appt", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Handles view appointment events.
	 */
	public void viewAppointment() {
		LOG.info("View appointment event received");
		final JSONObject request = new JSONObject();

		try {
			request.put("datetime", System.currentTimeMillis());
			send("view_appts", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Selects an appointment to reserve.
	 * 
	 * @param time
	 *            The time of the appointment, represented as a Unix timestamp
	 * @param doctorUsername
	 *            The username of the doctor to associate with this appointment
	 * @param reason
	 *            The reason the patient requested this appointment
	 */
	public void selectAppointment(long time, String doctorUsername,
			String reason) {
		LOG.info("Select appointment event received");
		final JSONObject request = new JSONObject();

		try {
			request.put("datetime", midnight + time);
			request.put("patient_username", username);
			request.put("doctor_username", doctorUsername);
			request.put("reason", reason);

			send("select_appt", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	
	
	public void readAppts(String id) {
		LOG.info("Read appointment event received");

		try {
			final JSONObject request = new JSONObject();
			request.put("appts", id);

			send("read_appts", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
		
	}

	/**
	 * @param patient
	 *            The username of the patient associated with this treatment
	 *            record
	 * @param doctor
	 *            The doctor associated with this treatment record (C/U only)
	 * @param staff
	 *            The staff member associated with this treatment record (C/U
	 *            only)
	 * @param doctorOrders
	 *            The doctor's orders
	 * @param symptoms
	 *            The patient's symptoms
	 * @param vitalsigns
	 *            The patient's vital signs
	 * @param diagnosis
	 *            The doctor's diagnosis
	 */
	public void createTreatmentRecord(String patient, String doctor,
			String staff, String doctorOrders, String symptoms,
			String vitalsigns, String diagnosis) {
		LOG.info("Create treatment record event received");
		final JSONObject request = new JSONObject();

		try {
			request.put("patient", patient);
			request.put("doctor", doctor);
			request.put("staff", staff);
			request.put("doctorOrders", doctorOrders);
			request.put("symptoms", symptoms);
			request.put("vitalsigns", vitalsigns);
			request.put("diagnosis", diagnosis);

			send("create_tr", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	
	public void readTreatmentRecord(String user) {
		LOG.info("Read user event received");

		try {
			final JSONObject request = new JSONObject();
			request.put("user", user);

			send("read_user", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	
	/**
	 * Handles view Treatment Record events.
	 */
	public void viewTreatmentRecord() {
		LOG.info("View Treatment Record event received");
		final JSONObject request = new JSONObject();

		try {
			request.put("datetime", System.currentTimeMillis());
			send("view_treatmentRecord", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	/**
	 * Requests the list of users from the server.
	 */
	public void viewUsers() {
		LOG.info("View users event received");

		try {
			send("view_users", new JSONObject());
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	/**
	 * Report
	 */
	public void viewReport() {
		LOG.info("View reports event received");

		try {
			send("view_report", new JSONObject());
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	/**
	 * Requests information on one user from the server.
	 * 
	 * @param user
	 *            The username of the user to request
	 */
	public void readUser(String user) {
		LOG.info("Read user event received");

		try {
			final JSONObject request = new JSONObject();
			request.put("user", user);

			send("read_user", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}
	

	/**
	 * Updates a user with a new password.
	 * 
	 * @param username
	 *            The username of the user to modify
	 * @param password
	 *            The new password
	 */
	public void updateUser(String username, String password) {
		LOG.info("Update user event received");

		try {
			final JSONObject fields = new JSONObject();
			fields.put("password", password);

			final JSONObject request = new JSONObject();
			request.put("user", username);
			request.put("fields", fields);

			send("update_user", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Deletes a user from the system.
	 * 
	 * @param username
	 *            The username of the user to delete
	 */
	public void deleteUser(String username) {
		LOG.info("Delete user event received (user=" + username);

		try {
			final JSONObject request = new JSONObject();
			request.put("user", username);

			send("delete_user", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Adds a user to the system.
	 * 
	 * @param username
	 *            The username of the new user
	 * @param type
	 *            The type of user to create
	 * @param password
	 *            The password of the new user
	 */
	public void createUser(String username, String type, String password) {
		LOG.info("Create user event received (user=" + username);

		try {
			final JSONObject request = new JSONObject();
			request.put("user", username);
			request.put("pass", password);
			request.put("type", type);

			send("create_user", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Requests a list of invoices from the server.
	 */
	public void viewInvoiceList() {
		LOG.info("View invoice list action received");

		try {
			send("view_invoices", new JSONObject());
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Requests a single invoice from the server.
	 * 
	 * @param id
	 *            The ID of the invoice to request
	 */
	public void viewInvoice(int id) {
		LOG.info("View invoice action received");

		try {
			final JSONObject request = new JSONObject();
			request.put("id", id);

			send("view_invoice", request);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Error building request", e);
		}
	}

	/**
	 * Logs out the user.
	 */
	public void logout() {
		username = null;
		password = null;
		usertype = null;

		MainFrame.getInstance().show("login", null);
	}

	/**
	 * Executes the code to send the request to the server.
	 * 
	 * @param actionCode
	 *            The action code to add to the request
	 * @param request
	 *            The request to send to the server.
	 * @throws JSONException
	 *             If an error occurs while handling the request object
	 */
	private void send(String actionCode, JSONObject request)
			throws JSONException {
		if (username != null && password != null) {
			request.put("username", username);
			request.put("password", password);
		}

		request.put("action", actionCode);

		final ClientConnectionHandler handler = new ClientConnectionHandler(
				request, this);
		final Thread thread = new Thread(handler, "Handler Thread [action="
				+ request.getString("action") + "]");

		LOG.info("Sending request " + request);
		thread.start();
	}

	/**
	 * Returns a string representation of this class.
	 * 
	 * @return A string representation of this class
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Presenter (running in thread "
				+ Thread.currentThread().getName() + ")";
	}

	/**
	 * Returns the singleton instance of this class.
	 * 
	 * @return An instance of this class
	 */
	public static Presenter getInstance() {
		if (Instance == null) {
			Instance = new Presenter();
		}

		return Instance;
	}


}
